package ua.com.stormlabs.tiny.licenseserver.controllers;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import ua.com.stormlabs.tiny.licenseserver.beans.User;
import ua.com.stormlabs.tiny.licenseserver.service.UsersService;
import ua.com.stormlabs.tiny.licenseserver.utils.ArgumentGuard;

import java.util.Date;

/**
 * @author Denis Bondarenko
 * @version 1.0 Nov 16, 2010
 */
public class UserRequestsValidator {

    private static final Logger log = LoggerFactory.getLogger(UserRequestsValidator.class);

    @Autowired
    private UsersService usersService;

    private String privateKey;
//    private static final String ANY = "*";

    public String buildResponse(String userName, String key, String mac, String opaque) {
        ArgumentGuard.checkNotNull(userName, "userName");
        ArgumentGuard.checkNotBlank(userName, "userName");
        ArgumentGuard.checkNotNull(key, "key");
        ArgumentGuard.checkNotBlank(key, "key");
        ArgumentGuard.checkNotNull(opaque, "opaque");
        ArgumentGuard.checkNotBlank(opaque, "opaque");
        ArgumentGuard.checkNotNull(mac, "mac");
        ArgumentGuard.checkNotBlank(mac, "mac");

        User user = null;
        try {
            user = usersService.getUserByName(userName);
            if (!user.getMacChecking()) {
                if (isValidNonMac(user, key, opaque, mac)) {
                    return buildValidResponse(userName, user.getLicenseCode(), mac, opaque);
                } else {
                    return buildFakeResponse();
                }
            } else {
                if (user.getMac() == null || user.getMac().isEmpty()) {
                    usersService.updateUserMac(user.getId(), mac);
                    user.setMac(mac);
                    if (isValid(user, key, opaque)) {
                        return buildLicenseActivatedResponse(userName, user.getLicenseCode(), user.getMac(), opaque);
                    } else {
                        return buildFakeResponse();
                    }
                }

                if (!user.getMac().equals(mac)) {
                    log.error("User " + userName + "mac: " + mac + " already activated with mac: " +
                            user.getMac());
                    return buildAnotherMacActivatedResponse(user.getLicenseCode(), user.getUserName(), opaque);
                }
            }
        } catch (Exception e) {
            log.error("Failed to find user for name [" + userName + "]. Error: " + e, e);
        }
        if (isValid(user, key, opaque)) {
            return buildValidResponse(userName, user.getLicenseCode(), user.getMac(), opaque);
        } else {
            return buildFakeResponse();
        }
    }

    private String buildAnotherMacActivatedResponse(String licenseCode, String userName, String opaque) {
        return DigestUtils.md5Hex(userName.trim().toLowerCase() + licenseCode.trim().toLowerCase() +
                opaque.trim().toLowerCase() + "{" + privateKey + "}");
    }

    public boolean isValidUser(String userName, String key, String opaque, String mac) {
        ArgumentGuard.checkNotNull(userName, "userName");
        ArgumentGuard.checkNotBlank(userName, "userName");
        ArgumentGuard.checkNotNull(key, "key");
        ArgumentGuard.checkNotBlank(key, "key");
        ArgumentGuard.checkNotNull(opaque, "opaque");
        ArgumentGuard.checkNotBlank(opaque, "opaque");
        ArgumentGuard.checkNotNull(mac, "mac");
        ArgumentGuard.checkNotBlank(mac, "mac");

        User user = null;
        try {
            user = usersService.getUserByName(userName);
            if (!user.getMacChecking()) {
                return isValidNonMac(user, key, opaque, mac);
            } else {
                if (user.getMac() == null || user.getMac().isEmpty()) {
                    usersService.updateUserMac(user.getId(), mac);
                    user.setMac(mac);
                    return isValid(user, key, opaque);
                }

                if (!user.getMac().equals(mac)) {
                    log.error("User " + userName + "mac: " + mac + " already activated with mac: " +
                            user.getMac());
                    return false;
                }
            }
        } catch (Exception e) {
            log.error("Failed to find user for name [" + userName + "]. Error: " + e, e);
        }

        return false;
    }

    private String buildFakeResponse() {
        log.info("Generating fake response");
        return RandomStringUtils.randomAlphanumeric(32);
    }

    private String buildValidResponse(String userName, String licenseCode, String mac, String opaque) {
        log.info("Generating valid response");
        String line = userName.trim().toLowerCase() + licenseCode.trim().toLowerCase() + mac.toLowerCase() +
                opaque.toLowerCase() + "{" + privateKey + "}";
        return DigestUtils.md5Hex(line);
    }

    private String buildLicenseActivatedResponse(String userName, String licenseCode, String mac, String opaque) {
        log.info("Generating valid response");
        String line = userName.trim().toLowerCase() + licenseCode.trim().toLowerCase() + mac.toLowerCase() +
                opaque.toLowerCase() + "{ACTIVATED}" + "{" + privateKey + "}";
        return DigestUtils.md5Hex(line);
    }

    protected static boolean isValid(User user, String key, String opaque) {
        if (user == null) {
            log.info("Null user is invalid");
            return false;
        }
        String expectedKey = DigestUtils.md5Hex("userName=" + user.getUserName() + "licenseCode=" + user.getLicenseCode()
                + "mac=" + user.getMac() + "opaque=" + opaque);
        if (!key.equals(expectedKey)) {
            log.info("License code is invalid. Expected: " + expectedKey + ", actual: " + key);
            return false;
        }
        Date now = new Date();
        if (user.getExpireDate().before(now)) {
            log.info("License expired. ExpireDate: " + user.getExpireDate() + ", now: " + now);
            return false;
        }

        log.info("License is valid");
        return true;
    }

    protected static boolean isValidNonMac(User user, String key, String opaque, String mac) {
        if (user == null) {
            log.info("Null user is invalid");
            return false;
        }
        String expectedKey = DigestUtils.md5Hex("userName=" + user.getUserName() + "licenseCode=" + user.getLicenseCode()
                + "mac=" + mac + "opaque=" + opaque);
        if (!key.equals(expectedKey)) {
            log.info("License code is invalid. Expected: " + expectedKey + ", actual: " + key);
            return false;
        }
        Date now = new Date();
        if (user.getExpireDate().before(now)) {
            log.info("License expired. ExpireDate: " + user.getExpireDate() + ", now: " + now);
            return false;
        }

        log.info("License is valid");
        return true;
    }

    public void setPrivateKey(String privateKey) {
        this.privateKey = privateKey;
    }
}
